﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;

namespace Nirvana
{
    [ExecuteInEditMode]
    public class GameObjectAttach : MonoBehaviour
    {
        sealed class ObjectAttachRef
        {
            public GameObject parent;
            public int instanceId;
            public void Free()
            {
                if (parent != null)
                {
                    Singleton<GameObjectPool>.Instance.Free(this.parent, false);
                }
                else
                {
                    Singleton<GameObjectPool>.Instance.Free(this.instanceId);
                }
            }
        }

        [SerializeField]
        [AssetType(typeof(GameObject))]
        private AssetID assetid;

        [Tooltip("Whether to sync the layer.")]
        [SerializeField]
        private bool synclayer = true;

        [SerializeField]
        private bool simulateInEditMode = true;
        private bool IsFree;
        private bool IsStartLoad;
   
        private GameObject root;
        private Action<GameObject> callback;

        public AssetID AssetId
        {
            get
            {
                return this.assetid;
            }
            set
            {
                if (!this.assetid.Equals(value))
                {
                    this.assetid = value;
                    if (base.isActiveAndEnabled)
                    {
                        this.IsStartLoad = true;
                    }
                }
            }
        }

        public void Wait(Action<GameObject> cb)
        {
            if (this.root == null)
            {
                this.callback = cb;
            }
            else
            {
                cb(this.root);
            }
        }

        private void OnEnable()
        {
            if (!this.IsFree && !this.assetid.IsEmpty)
            {
                this.IsFree = true;
                this.IsStartLoad = true;
            }
        }

        private void OnDisable()
        {
            this.IsFree = false;
            Free();
            PreviewObject previewobj = base.GetComponent<PreviewObject>();
            if (previewobj != null) previewobj.ClearPreview();

        }

        private void Update()
        {
            if (this.IsStartLoad)
            {
                this.IsStartLoad = false;
                this.UpdatePreviewObj();
            }
        }

#if UNITY_EDITOR
        private void OnValidate()
        {
            if (!this.assetid.IsEmpty && base.isActiveAndEnabled)
            {
                this.IsFree = true;
                this.UpdatePreviewObj();
            }
            else if (Application.isPlaying)
            {
                if (this.root != null)
                {
                    ObjectAttachRef u = new ObjectAttachRef();
                    u.parent = this.root;
                    this.root = null;
                    Scheduler.Delay(new Action(u.Free));
                }
            }
            else
            {
                PreviewObject component = base.GetComponent<PreviewObject>();
                if (component != null)
                {
                    component.ClearPreview();
                }
            }
        }
#endif


        private void UpdatePreviewObj()
        {
            this.Free();
            if (this.assetid.IsEmpty)
            {
                return;
            }
            if (Application.isPlaying)
            {
                Scheduler.RunCoroutine(this.WaitLoadObject(this.assetid));
            }
            else
            {
                GameObject gameObject = this.assetid.LoadObject<GameObject>();
                if (gameObject != null)
                {
                    GameObject preview = Singleton<PrefabPool>.Instance.Instantiate(gameObject);
                    PreviewObject orAddComponentDontSave = this.GetOrAddComponentDontSave<PreviewObject>();
                    orAddComponentDontSave.SimulateInEditMode = this.simulateInEditMode;
                    orAddComponentDontSave.SetPreview(preview);
                }
            }
        }

        private IEnumerator WaitLoadObject(AssetID assetid)
        {
            WaitLoadGameObjectCache waitSpawnGameObject;
            if (InstantiateQueue.Global != null)
            {
                waitSpawnGameObject = Singleton<GameObjectPool>.Instance.SpawnAssetWithQueue(assetid, InstantiateQueue.Global, 0);
            }
            else
            {
                waitSpawnGameObject = Singleton<GameObjectPool>.Instance.SpawnAsset(assetid);
            }
            yield return waitSpawnGameObject;
            if (waitSpawnGameObject.Error != null)
            {
                Debug.LogWarningFormat("Load attach object from '{0}' failed: {1}", this.assetid, waitSpawnGameObject.Error);
                yield break;
            }
            this.Free();
            this.root = waitSpawnGameObject.GetObject();
            if (this.root == null)
            {
                Debug.LogWarningFormat("Load attach object from '{0}' failed.", this.assetid);
                yield break;
            }
            if (!this.IsFree)
            {
                this.Free();
                yield break;
            }
            if (this.assetid.Equals(assetid))
            {
                this.root.transform.SetParent(base.transform, false);
                if (this.synclayer)
                {
                    this.root.SetLayerRecursively(base.gameObject.layer);
                }
                if (this.callback != null)
                {
                    this.callback(this.root);
                }
            }
            else
            {
                this.UpdatePreviewObj();
            }
            yield break;
        }


        private void Free()
        {
            if (this.root != null)
            {
                if (Application.isPlaying)
                {
                    ObjectAttachRef u = new ObjectAttachRef();
                    Assert.IsNotNull<GameObject>(this.root);
                    u.parent = this.root;
                    u.instanceId = this.root.GetInstanceID();
                    this.root = null;
                    Scheduler.Delay(new Action(u.Free));
                }
                else
                {
                    UnityEngine.Object.DestroyImmediate(this.root);
                    this.root = null;
                }
            }
        }

        public void RefreshAssetBundleName()
        {
            this.assetid.RefreshAssetBundleName();
        }

        private void OnDestroy()
        {
            if (this.root != null)
            {
                if (Application.isPlaying)
                {
                    Singleton<GameObjectPool>.Instance.Free(this.root, false);
                    this.root = null;
                }
                else
                {
                    UnityEngine.Object.DestroyImmediate(this.root);
                    this.root = null;
                }
            }
        }

    }
}

